package com.blockchainenergy.blockChain;

import com.alibaba.fastjson.JSON;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 存储操作链的类
 **/
public class DataChain {
    //     单例模式类
    private static volatile DataChain dataChain;

    // 创建单例对象
    public static DataChain getInstance() throws Exception {
        if (dataChain == null) {
            synchronized (DataChain.class) {
                if (dataChain == null) {
                    dataChain = new DataChain();
                }
            }
        }
        return dataChain;
    }

    // 存储区块链
    private List<DataBlock> chain;

    //  决定了挖矿的困难程度
    private final static String difficulty = "0000";

    private DataChain() {
        // 初始化区块链以及当前的交易信息列表
        chain = new ArrayList<>();

        SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
        long l = Long.parseLong(df.format(new Date()));// 转成
        // 创建创世区块
        newBlock(100L, "0", l);
    }

    public List<DataBlock> getChain() {
        return chain;
    }

    /**
     * 得到区块链中的最后一个区块
     */
    public DataBlock lastBlock() {
        return getChain().get(getChain().size() - 1);
    }

    public String getLastBlockHash() {
        return (String) hash(lastBlock());
    }

    /**
     * 新建区块
     */
    public DataBlock newBlock(Long proof, String previous_hash, Long l) {
        // if(proof==null) proof = proofOfWork(Long.parseLong(lastBlock().get("proof") + ""));
        DataBlock block = new DataBlock();
        // 设置区块类型
        int size = getChain().size();
        block.setIndex(size);
        block.setTimestamp(System.currentTimeMillis());
        block.setData(l);
        block.setPrevious_hash(previous_hash);
        // 如果没有传递上一个区块的hash就计算出区块链中最后一个区块的hash
        getChain().add(block);

        return block;
    }

    /**
     * 带carbon的块
     */
    private DataBlock newBlock(Long proof, String previous_hash, Carbon carbon) {
        // if(proof==null) proof = proofOfWork(Long.parseLong(lastBlock().get("proof") + ""));
        DataBlock block = new DataBlock();
        // 设置区块类型
        int size = getChain().size();
        block.setIndex(size);
        block.setTimestamp(System.currentTimeMillis());
        block.setCarbon(carbon);
        block.setPrevious_hash(previous_hash);
        // 如果没有传递上一个区块的hash就计算出区块链中最后一个区块的hash
        getChain().add(block);

        return block;
    }

    public void addBlock(long l) {
        newBlock(null, getLastBlockHash(), l);
    }

    /**
     * hash
     */
    public static Object hash(DataBlock block) {
        return new Encrypt().getSHA256(JSON.toJSONString(block));
    }

    /**
     * 工作量证明
     */
    public Long proofOfWork(Long last_proof) {
        Long proof = 0L;
        while (!validProof(last_proof, proof)) {
            proof += 1;
        }
        return proof;
    }

    /**
     * 验证工作量证明
     */
    public boolean validProof(Long last_proof, Long proof) {
        String guess = last_proof + "" + proof;
        //使用SHA256
        String guess_hash = new Encrypt().getSHA256(guess);
        return guess_hash.startsWith(difficulty);
    }

    /**
     * 验证区块链
     **/
    public boolean validChain(List<DataBlock> chain) {
        DataBlock lastBlock = chain.get(0);
        int currentIndex = 1;
        while (currentIndex < chain.size()) {
            DataBlock block = chain.get(currentIndex);
            // 检查block的hash是否正确
            if (!block.getPrevious_hash().equals(hash(lastBlock))) {
                return false;
            }
            lastBlock = block;
            currentIndex++;
        }
        return true;
    }

    public void newCarbonBlock(Carbon carbon) {
        newBlock(null, getLastBlockHash(), carbon);
    }
}

